﻿using System;
#if WINDOWSPHONE || WINDOWS_PHONE71
using System.Diagnostics;
#endif

namespace WindowsPhoneEssentials.Diagnostics
{
    public class TraceScope : IDisposable
    {
        private readonly TraceLevel traceLevel;
        private readonly Func<string> task;
        private readonly Type sourceType;
        private readonly Stopwatch stopwatch;
        private bool hasChildTraces;

        public TraceScope(object source, TraceLevel traceLevel, Func<string> task) :
            this(source == null ? typeof(TraceScope) : source.GetType(), traceLevel, task)
        { }

        public TraceScope(Type sourceType, TraceLevel traceLevel, Func<string> task)
        {
            if (Trace.IgnoreTraceFrom(traceLevel) || Trace.Appenders.Count == 0) return;

            this.traceLevel = traceLevel;
            this.task = task;
            this.sourceType = sourceType;
            stopwatch = new Stopwatch();

            Trace.BeginTraceScope(write =>
                                      {
                                          write(traceLevel, sourceType, task());
                                          write(traceLevel, sourceType, "{");
                                          Trace.Indent();
                                          hasChildTraces = true;
                                      });

            stopwatch.Start();
        }

        public void Dispose()
        {
            if (stopwatch == null) return;
            stopwatch.Stop();
            if (hasChildTraces)
            {
                Trace.Unindent();
                Write(() => string.Format("}} {0}ms", stopwatch.ElapsedMilliseconds));
            }
            else
            {
                Trace.ClearTraceScopeCallback();
                Write(() => string.Format("{0}... {1}ms", task(), stopwatch.ElapsedMilliseconds));
            }
        }

        private void Write(Func<string> log)
        {
            switch (traceLevel)
            {
                case TraceLevel.Debug:
                    Trace.WriteDebug(sourceType, log);
                    break;
                case TraceLevel.Info:
                    Trace.WriteInfo(sourceType, log);
                    break;
                case TraceLevel.Warning:
                    Trace.WriteWarning(sourceType, log);
                    break;
                case TraceLevel.Error:
                    Trace.WriteError(sourceType, log);
                    break;
                default:
                    Trace.WriteError(this, ()=>"TraceScope doesn't recognise trace level " + traceLevel);
                    break;
            }
        }
    }
}